昨天我們發現浮點數常是一個逼近值,因此有時候比較是否相等的結果會令你出乎意料。
那麼,今天就來研究,我們要怎麼克服這個「相等」的問題!
回憶一下昨天發現的怪現象:
x = 0.1 + 0.1 + 0.1
y = 0.3
x == y
False
這是因為 0.1
和 0.3
其實是逼近值,並非完全精確。多看幾個小數點位數可以發現:
print('0.1 --> {0:.25f}'.format(0.1))
print('x --> {0:.25f}'.format(x))
print('y --> {0:.25f}'.format(y))
0.1 --> 0.1000000000000000055511151
x --> 0.3000000000000000444089210
y --> 0.2999999999999999888977698
那有沒有方法克服這個情況呢?一個作法是使用 round
方法,四捨五入到小數點後某個位數再比較:
x = 0.1 + 0.1 + 0.1
y = 0.3
round(x, 5) == round(y, 5)
True
使用 round
讓我們以一個近似值比較是否相等。
但有個問題,假設A、B兩個數字很大:
A = 50000000.00001
B = 50000000.00002
round(A, 5) == round(B, 5)
False
我們發現使用 round
四捨五入到小數點後五位,他們並不相等。
但其實 A, B 是很接近的,因為他們其實是很大的數字,小數點後面五位可以說幾乎不重要,我們可以把兩者相除看看:
print(A / B)
0.9999999999998
幾乎等於 1,由於這兩個浮點數本來就是近似值,我們可能希望將它們兩個視為「相等」。
那怎麼辦呢?我們來介紹一個在 math
模組裡面的 isclose
方法:
from math import isclose
help(isclose)
Help on built-in function isclose in module math:
isclose(a, b, *, rel_tol=1e-09, abs_tol=0.0)
Determine whether two floating point numbers are close in value.
rel_tol
maximum difference for being considered "close", relative to the
magnitude of the input values
abs_tol
maximum difference for being considered "close", regardless of the
magnitude of the input values
Return True if a is close in value to b, and False otherwise.
For the values to be considered close, the difference between them
must be smaller than at least one of the tolerances.
-inf, inf and NaN behave similarly to the IEEE 754 Standard. That
is, NaN is not close to anything, even itself. inf and -inf are
only close to themselves.
x = 0.1 + 0.1 + 0.1
y = 0.3
isclose(x, y)
True
isclose
方法有兩個引數: rel_tol
和 abs_tol
.
rel_tol
是指A、B兩個數字的相對差異,小於這個差異可視為兩數接近(close),預設值是 A, B 較大者乘上 1e-09。
abs_tol
是指A、B兩個數字的絕對差異,跟 A, B 多大多小無關,是一個定值,兩者相減小於此值視為接近,預設值是 0。
看一下以下的例子,x, y 算很接近:
x = 123456789.01
y = 123456789.02
但下面這兩個就差滿多了:
x = 0.01
y = 0.02
上面兩個例子的 x, y 都是相差 0.01,但因為數字本身大小不同使得第一對很接近,第二對有差距。
這時候相對差異就很好用:
isclose(123456789.01, 123456789.02, rel_tol=0.01)
True
isclose(0.01, 0.02, rel_tol=0.01)
False
但是要小心了!當數字很接近零的時候,相對差異永遠微不足道————這使得兩個數字永遠無法「接近」。
x = 0.000000000001
y = 0.0000000000005
isclose(x, y, rel_tol=0.01)
False
上述兩個數字幾乎等於零,我們希望視為兩個數字接近,相對差異已經失效了,那怎麼辦?
讓絕對差異派上用場!
isclose(x, y, abs_tol=0.0001, rel_tol=0)
True
x, y 兩者相減大於絕對差異的設定 0.0001,因而兩者視為接近。
善用相對差異跟絕對差異的配合,我們就可以做出各種浮點數都適用的「相等」判斷了!
x = 0.0000001
y = 0.0000002
a = 123456789.01
b = 123456789.02
print('x = y:', isclose(x, y, abs_tol=0.0001, rel_tol=0.01))
print('a = b:', isclose(a, b, abs_tol=0.0001, rel_tol=0.01))
x = y: True
a = b: True
明天繼續浮點數之旅,我們明~天~見!
參考:Python 3: Deep Dive (Part 1 - Functional)